﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace Liquid.Collections {
	/// <summary>
	/// A collection that has events for changes.
	/// </summary>
	/// <typeparam name="T">The type of the items in the collection.</typeparam>
	public class ChangeNotifyingCollection<T> : Collection<T>, IChangeNotifyingEnumerable<T> {
		/// <summary>
		/// Occurs when a set of items is added.
		/// </summary>
		public event ItemSetEventHandler<T> Added;
		/// <summary>
		/// Raises the <see cref="E:Added"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Liquid.Collections.ItemSetEventArgs&lt;T&gt;"/> instance containing the event data.</param>
		protected virtual void OnAdded(ItemSetEventArgs<T> e) {
			if (Added != null) Added(this, e);
		}

		/// <summary>
		/// Occurs when a set of items are removed.
		/// </summary>
		public event ItemSetEventHandler<T> Removed;
		/// <summary>
		/// Raises the <see cref="E:Removed"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Liquid.Collections.ItemSetEventArgs&lt;T&gt;"/> instance containing the event data.</param>
		protected virtual void OnRemoved(ItemSetEventArgs<T> e) {
			if (Removed != null) Removed(this, e);
		}

		/// <summary>
		/// Inserts an element into the <see cref="T:System.Collections.ObjectModel.Collection`1"/> at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
		/// <param name="item">The object to insert. The value can be null for reference types.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// 	<paramref name="index"/> is less than zero.-or-<paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
		protected override void InsertItem(int index, T item) {
			base.InsertItem(index, item);
			OnAdded(new ItemSetEventArgs<T>(item));
		}
		/// <summary>
		/// Replaces the element at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the element to replace.</param>
		/// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// 	<paramref name="index"/> is less than zero.-or-<paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
		protected override void SetItem(int index, T item) {
			base.SetItem(index, item);
			OnAdded(new ItemSetEventArgs<T>(item));
		}
		/// <summary>
		/// Removes all elements from the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
		/// </summary>
		protected override void ClearItems() {
			base.ClearItems();
			OnRemoved(new ItemSetEventArgs<T>(this));
		}
		/// <summary>
		/// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
		/// </summary>
		/// <param name="index">The zero-based index of the element to remove.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// 	<paramref name="index"/> is less than zero.-or-<paramref name="index"/> is equal to or greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
		protected override void RemoveItem(int index) {
			var item = this[index];
			base.RemoveItem(index);
			OnRemoved(new ItemSetEventArgs<T>(item));
		}
	}
}
